agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
Re: logtape.c stats don't account for unused "prefetched" block numbers
429+ messages / 4 participants
[nested] [flat]

* Re: logtape.c stats don't account for unused "prefetched" block numbers
@ 2020-09-01 23:36 Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-02 00:24 ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
  0 siblings, 1 reply; 429+ messages in thread

From: Alvaro Herrera @ 2020-09-01 23:36 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers

On 2020-Jul-30, Peter Geoghegan wrote:

> Commit 896ddf9b added prefetching to logtape.c to avoid excessive
> fragmentation in the context of hash aggs that spill and have many
> batches/tapes. Apparently the preallocation doesn't actually perform
> any filesystem operations, so the new mechanism should be zero
> overhead when "preallocated" blocks aren't actually used after all
> (right?). However, I notice that this breaks the statistics shown by
> things like trace_sort, and even EXPLAIN ANALYZE.
> LogicalTapeSetBlocks() didn't get the memo about preallocation.

This open item hasn't received any replies.  I think Peter knows how to
fix it already, but no patch has been posted ... It'd be good to get a
move on it.

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services





^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* Re: logtape.c stats don't account for unused "prefetched" block numbers
  2020-09-01 23:36 Re: logtape.c stats don't account for unused "prefetched" block numbers Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2020-09-02 00:24 ` Peter Geoghegan <pg@bowt.ie>
  2020-09-05 19:03   ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
  0 siblings, 1 reply; 429+ messages in thread

From: Peter Geoghegan @ 2020-09-02 00:24 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers

On Tue, Sep 1, 2020 at 4:36 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> This open item hasn't received any replies.  I think Peter knows how to
> fix it already, but no patch has been posted ... It'd be good to get a
> move on it.

I picked this up again today.

It's not obvious what we should do. It's true that the instrumentation
doesn't accurately reflect the on-disk temp file overhead. That is, it
doesn't agree with the high watermark temp file size I see in the
pgsql_tmp directory, which is a clear regression compared to earlier
releases (where tuplesort was the only user of logtape.c). But it's
also true that we need to use somewhat more temp file space for a
tuplesort in Postgres 13, because we use the preallocation stuff for
tuplesort -- though probably without getting any benefit for it.

I haven't figured out how to correct the accounting just yet. In fact,
I'm not sure that this isn't some kind of leak of blocks from the
freelist, which shouldn't happen at all. The code is complicated
enough that I wasn't able to work that out in the couple of hours I
spent on it today. I can pick it up again tomorrow.

BTW, this MaxAllocSize freeBlocksLen check is wrong -- doesn't match
the later repalloc allocation:

    if (lts->nFreeBlocks >= lts->freeBlocksLen)
    {
        /*
         * If the freelist becomes very large, just return and leak this free
         * block.
         */
        if (lts->freeBlocksLen * 2 > MaxAllocSize)
            return;

        lts->freeBlocksLen *= 2;
        lts->freeBlocks = (long *) repalloc(lts->freeBlocks,
                                            lts->freeBlocksLen * sizeof(long));
    }

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* Re: logtape.c stats don't account for unused "prefetched" block numbers
  2020-09-01 23:36 Re: logtape.c stats don't account for unused "prefetched" block numbers Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-02 00:24 ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
@ 2020-09-05 19:03   ` Peter Geoghegan <pg@bowt.ie>
  2020-09-08 17:27     ` Re: logtape.c stats don't account for unused "prefetched" block numbers Jeff Davis <pgsql@j-davis.com>
  2020-09-09 06:28     ` Re: logtape.c stats don't account for unused "prefetched" block numbers Jeff Davis <pgsql@j-davis.com>
  0 siblings, 2 replies; 429+ messages in thread

From: Peter Geoghegan @ 2020-09-05 19:03 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Jeff Davis <pgsql@j-davis.com>; pgsql-hackers

On Tue, Sep 1, 2020 at 5:24 PM Peter Geoghegan <pg@bowt.ie> wrote:
> On Tue, Sep 1, 2020 at 4:36 PM Alvaro Herrera <alvherre@2ndquadrant.com> wrote:
> > This open item hasn't received any replies.  I think Peter knows how to
> > fix it already, but no patch has been posted ... It'd be good to get a
> > move on it.
>
> I picked this up again today.

One easy way to get logtape.c to behave in the same way as Postgres 12
for a multi-pass external sort (i.e. to use fewer blocks and to report
the number of blocks used accurately) is to #define
TAPE_WRITE_PREALLOC_MIN and TAPE_WRITE_PREALLOC_MAX to 1. So it looks
like the problem is in the preallocation stuff added by commit
896ddf9b3cd, and not the new heap-based free list logic added by
commit c02fdc92230. That's good news, because it means that the
problem may be fairly well isolated -- commit 896ddf9b3cd was a pretty
small and isolated thing.

The comments in ltsWriteBlock() added by the 2017 bugfix commit
7ac4a389a7d clearly say that the zero block writing stuff is only
supposed to happen at the edge of a tape boundary, which ought to be
rare -- see the comment block in ltsWriteBlock(). And yet the new
preallocation stuff explicitly relies on that it writing zero blocks
much more frequently. I'm concerned that that can result in increased
and unnecessary I/O, especially for sorts, but also for hash aggs that
spill. I'm also concerned that having preallocated-but-allocated
blocks confuses the accounting used by
trace_sort/LogicalTapeSetBlocks().

Separately, it's possible to make the
trace_sort/LogicalTapeSetBlocks() instrumentation agree with the
filesystem by replacing the use of nBlocksAllocated within
LogicalTapeSetBlocks() with nBlocksWritten -- that seems to make the
instrumentation correct without changing the current behavior at all.
But I'm not ready to endorse that approach, since it's not quite clear
what nBlocksAllocated and nBlocksWritten mean right now -- those two
fields were both added by the aforementioned 2017 bugfix commit, which
introduced the "allocated vs written" distinction in the first place.

We should totally disable the preallocation stuff for external sorts
in any case. External sorts are naturally characterized by relatively
large, distinct batching of reads and writes -- preallocation cannot
help.

-- 
Peter Geoghegan





^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* Re: logtape.c stats don't account for unused "prefetched" block numbers
  2020-09-01 23:36 Re: logtape.c stats don't account for unused "prefetched" block numbers Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-02 00:24 ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
  2020-09-05 19:03   ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
@ 2020-09-08 17:27     ` Jeff Davis <pgsql@j-davis.com>
  1 sibling, 0 replies; 429+ messages in thread

From: Jeff Davis @ 2020-09-08 17:27 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers

On Sat, 2020-09-05 at 12:03 -0700, Peter Geoghegan wrote:
> We should totally disable the preallocation stuff for external sorts
> in any case. External sorts are naturally characterized by relatively
> large, distinct batching of reads and writes -- preallocation cannot
> help.

Patch attached to disable preallocation for Sort.

I'm still looking into the other concerns.

Regards,
	Jeff Davis



Attachments:

  [text/x-patch] sort-no-prealloc.patch (5.7K, ../../0383d21d58c6fd7d2426d7730c50c6743a5ba303.camel@j-davis.com/2-sort-no-prealloc.patch)
  download | inline diff:
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 9776263ae75..f74d4841f17 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2882,7 +2882,7 @@ hashagg_tapeinfo_init(AggState *aggstate)
 	HashTapeInfo *tapeinfo = palloc(sizeof(HashTapeInfo));
 	int			init_tapes = 16;	/* expanded dynamically */
 
-	tapeinfo->tapeset = LogicalTapeSetCreate(init_tapes, NULL, NULL, -1);
+	tapeinfo->tapeset = LogicalTapeSetCreate(init_tapes, true, NULL, NULL, -1);
 	tapeinfo->ntapes = init_tapes;
 	tapeinfo->nfreetapes = init_tapes;
 	tapeinfo->freetapes_alloc = init_tapes;
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index bbb01f6d337..8ec224b62de 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -212,6 +212,7 @@ struct LogicalTapeSet
 	long	   *freeBlocks;		/* resizable array holding minheap */
 	long		nFreeBlocks;	/* # of currently free blocks */
 	Size		freeBlocksLen;	/* current allocated length of freeBlocks[] */
+	bool		enable_prealloc;	/* preallocate write blocks? */
 
 	/* The array of logical tapes. */
 	int			nTapes;			/* # of logical tapes in set */
@@ -220,6 +221,7 @@ struct LogicalTapeSet
 
 static void ltsWriteBlock(LogicalTapeSet *lts, long blocknum, void *buffer);
 static void ltsReadBlock(LogicalTapeSet *lts, long blocknum, void *buffer);
+static long ltsGetBlock(LogicalTapeSet *lts, LogicalTape *lt);
 static long ltsGetFreeBlock(LogicalTapeSet *lts);
 static long ltsGetPreallocBlock(LogicalTapeSet *lts, LogicalTape *lt);
 static void ltsReleaseBlock(LogicalTapeSet *lts, long blocknum);
@@ -373,8 +375,20 @@ parent_offset(unsigned long i)
 }
 
 /*
- * Select the lowest currently unused block by taking the first element from
- * the freelist min heap.
+ * Get the next block for writing.
+ */
+static long
+ltsGetBlock(LogicalTapeSet *lts, LogicalTape *lt)
+{
+	if (lts->enable_prealloc)
+		return ltsGetPreallocBlock(lts, lt);
+	else
+		return ltsGetFreeBlock(lts);
+}
+
+/*
+ * Select the lowest currently unused block from the tape set's global free
+ * list min heap.
  */
 static long
 ltsGetFreeBlock(LogicalTapeSet *lts)
@@ -430,7 +444,8 @@ ltsGetFreeBlock(LogicalTapeSet *lts)
 
 /*
  * Return the lowest free block number from the tape's preallocation list.
- * Refill the preallocation list if necessary.
+ * Refill the preallocation list with blocks from the tape set's free list if
+ * necessary.
  */
 static long
 ltsGetPreallocBlock(LogicalTapeSet *lts, LogicalTape *lt)
@@ -671,8 +686,8 @@ ltsInitReadBuffer(LogicalTapeSet *lts, LogicalTape *lt)
  * infrastructure that may be lifted in the future.
  */
 LogicalTapeSet *
-LogicalTapeSetCreate(int ntapes, TapeShare *shared, SharedFileSet *fileset,
-					 int worker)
+LogicalTapeSetCreate(int ntapes, bool preallocate, TapeShare *shared,
+					 SharedFileSet *fileset, int worker)
 {
 	LogicalTapeSet *lts;
 	int			i;
@@ -689,6 +704,7 @@ LogicalTapeSetCreate(int ntapes, TapeShare *shared, SharedFileSet *fileset,
 	lts->freeBlocksLen = 32;	/* reasonable initial guess */
 	lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
 	lts->nFreeBlocks = 0;
+	lts->enable_prealloc = preallocate;
 	lts->nTapes = ntapes;
 	lts->tapes = (LogicalTape *) palloc(ntapes * sizeof(LogicalTape));
 
@@ -782,7 +798,7 @@ LogicalTapeWrite(LogicalTapeSet *lts, int tapenum,
 		Assert(lt->firstBlockNumber == -1);
 		Assert(lt->pos == 0);
 
-		lt->curBlockNumber = ltsGetPreallocBlock(lts, lt);
+		lt->curBlockNumber = ltsGetBlock(lts, lt);
 		lt->firstBlockNumber = lt->curBlockNumber;
 
 		TapeBlockGetTrailer(lt->buffer)->prev = -1L;
@@ -806,7 +822,7 @@ LogicalTapeWrite(LogicalTapeSet *lts, int tapenum,
 			 * First allocate the next block, so that we can store it in the
 			 * 'next' pointer of this block.
 			 */
-			nextBlockNumber = ltsGetPreallocBlock(lts, lt);
+			nextBlockNumber = ltsGetBlock(lts, lt);
 
 			/* set the next-pointer and dump the current block. */
 			TapeBlockGetTrailer(lt->buffer)->next = nextBlockNumber;
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 3c49476483b..cbda911f465 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -2591,7 +2591,7 @@ inittapes(Tuplesortstate *state, bool mergeruns)
 	/* Create the tape set and allocate the per-tape data arrays */
 	inittapestate(state, maxTapes);
 	state->tapeset =
-		LogicalTapeSetCreate(maxTapes, NULL,
+		LogicalTapeSetCreate(maxTapes, false, NULL,
 							 state->shared ? &state->shared->fileset : NULL,
 							 state->worker);
 
@@ -4657,8 +4657,9 @@ leader_takeover_tapes(Tuplesortstate *state)
 	 * randomAccess is disallowed for parallel sorts.
 	 */
 	inittapestate(state, nParticipants + 1);
-	state->tapeset = LogicalTapeSetCreate(nParticipants + 1, shared->tapes,
-										  &shared->fileset, state->worker);
+	state->tapeset = LogicalTapeSetCreate(nParticipants + 1, false,
+										  shared->tapes, &shared->fileset,
+										  state->worker);
 
 	/* mergeruns() relies on currentRun for # of runs (in one-pass cases) */
 	state->currentRun = nParticipants;
diff --git a/src/include/utils/logtape.h b/src/include/utils/logtape.h
index 39a99174afe..da5159e4c6c 100644
--- a/src/include/utils/logtape.h
+++ b/src/include/utils/logtape.h
@@ -54,7 +54,8 @@ typedef struct TapeShare
  * prototypes for functions in logtape.c
  */
 
-extern LogicalTapeSet *LogicalTapeSetCreate(int ntapes, TapeShare *shared,
+extern LogicalTapeSet *LogicalTapeSetCreate(int ntapes, bool preallocate,
+											TapeShare *shared,
 											SharedFileSet *fileset, int worker);
 extern void LogicalTapeSetClose(LogicalTapeSet *lts);
 extern void LogicalTapeSetForgetFreeSpace(LogicalTapeSet *lts);


^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* Re: logtape.c stats don't account for unused "prefetched" block numbers
  2020-09-01 23:36 Re: logtape.c stats don't account for unused "prefetched" block numbers Alvaro Herrera <alvherre@2ndquadrant.com>
  2020-09-02 00:24 ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
  2020-09-05 19:03   ` Re: logtape.c stats don't account for unused "prefetched" block numbers Peter Geoghegan <pg@bowt.ie>
@ 2020-09-09 06:28     ` Jeff Davis <pgsql@j-davis.com>
  1 sibling, 0 replies; 429+ messages in thread

From: Jeff Davis @ 2020-09-09 06:28 UTC (permalink / raw)
  To: Peter Geoghegan <pg@bowt.ie>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: pgsql-hackers

On Sat, 2020-09-05 at 12:03 -0700, Peter Geoghegan wrote:
> The comments in ltsWriteBlock() added by the 2017 bugfix commit
> 7ac4a389a7d clearly say that the zero block writing stuff is only
> supposed to happen at the edge of a tape boundary, which ought to be
> rare -- see the comment block in ltsWriteBlock(). And yet the new
> preallocation stuff explicitly relies on that it writing zero blocks
> much more frequently. I'm concerned that that can result in increased
> and unnecessary I/O, especially for sorts, but also for hash aggs
> that
> spill. I'm also concerned that having preallocated-but-allocated
> blocks confuses the accounting used by
> trace_sort/LogicalTapeSetBlocks().

Preallocation showed significant gains for HashAgg, and BufFile doesn't
support sparse writes. So, for HashAgg, it seems like we should just
update the comment and consider it the price of using BufFile.

(Aside: is there a reason why BufFile doesn't support sparse writes, or
is it just a matter of implementation?)

For Sort, we can just disable preallocation.

> Separately, it's possible to make the
> trace_sort/LogicalTapeSetBlocks() instrumentation agree with the
> filesystem by replacing the use of nBlocksAllocated within
> LogicalTapeSetBlocks() with nBlocksWritten -- that seems to make the
> instrumentation correct without changing the current behavior at all.
> But I'm not ready to endorse that approach, since it's not quite
> clear
> what nBlocksAllocated and nBlocksWritten mean right now -- those two
> fields were both added by the aforementioned 2017 bugfix commit,
> which
> introduced the "allocated vs written" distinction in the first place

Right now, it seems nBlocksAllocated means "number of blocks returned
by ltsGetFreeBlock(), plus nHoleBlocks".

nBlocksWritten seems to mean "the logical size of the BufFile". The
BufFile can have holes in it after concatenation, but from the
perspective of logtape.c, nBlocksWritten seems like a better fit for
instrumentation purposes. So I'd be inclined to return (nBlocksWritten
- nHoleBlocks).

The only thing I can think of that would be better is if BufFile
tracked for itself the logical vs. physical size, which might be a good
improvement to make (and would mean that logtape.c wouldn't be
responsible for tracking the holes itself).

Thoughts?

Regards,
	Jeff Davis







^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v1 1/5] Allow to use multiple shared memory mappings
@ 2024-10-09 13:41 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2024-10-09 13:41 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory slot.
There is only fixed amount of available slots, currently only one main
shared memory slot is allocated. A new shared memory API is introduces,
extended with a slot as a new parameter. As a path of least resistance,
the original API is kept in place, utilizing the main shared memory slot.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   2 +-
 src/backend/storage/ipc/ipci.c      |  61 ++++++------
 src/backend/storage/ipc/shmem.c     | 133 ++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  10 ++
 13 files changed, 258 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 64186ec0a7..b97723d2ed 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 5b88a92bc9..8ef95b12c9 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSlot(PGSemaphoreShmemSize(maxSemas), shmem_slot);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 362a37d3b3..065a5b63ac 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_slot;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping slots */
+static int next_free_slot = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_slot)
+{
+	switch (shmem_slot)
+	{
+		case MAIN_SHMEM_SLOT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_slot; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "slot[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_slot), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("slot[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_slot)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_slot];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_slot = next_free_slot;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_slot++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_slot;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_slot; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index f2b54bdfda..d62084cc0d 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_slot)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index b06e4b8452..2aabd4a77f 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -68,7 +68,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 35fa2e1dda..8224015b53 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -88,7 +88,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_slot)
 {
 	Size		size;
 	int			numSemas;
@@ -202,33 +202,36 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int slot = 0; slot < ANON_MAPPINGS; slot++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, slot);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSlot(seghdr, slot);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, slot);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSlot(slot);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -359,7 +362,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SLOT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 6d5f083986..c670b9cf43 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,17 +75,12 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSlot(Size size, Size *allocated_size,
+								 int shmem_slot);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
-
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
+ShmemSegment Segments[ANON_MAPPINGS];
 
 static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 
@@ -99,11 +94,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(void *seghdr)
 {
-	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	InitShmemAccessInSlot(seghdr, MAIN_SHMEM_SLOT);
+}
 
-	ShmemSegHdr = shmhdr;
-	ShmemBase = (void *) shmhdr;
-	ShmemEnd = (char *) ShmemBase + shmhdr->totalsize;
+void
+InitShmemAccessInSlot(void *seghdr, int shmem_slot)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_slot];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +115,13 @@ InitShmemAccess(void *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSlot(MAIN_SHMEM_SLOT);
+}
+
+void
+InitShmemAllocationInSlot(int shmem_slot)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_slot].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +130,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_slot].ShmemLock = (slock_t *) ShmemAllocUnlockedInSlot(sizeof(slock_t), shmem_slot);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_slot].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +157,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocInSlot(Size size, int shmem_slot)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +197,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSlot(size, allocated_size, MAIN_SHMEM_SLOT);
+}
+
+static void *
+ShmemAllocRawInSlot(Size size, Size *allocated_size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +222,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_slot].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_slot].ShmemSegHdr->totalsize)
 	{
-		newSpace = (void *) ((char *) ShmemBase + newStart);
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
+		Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_slot].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +255,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSlot(size, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemAllocUnlockedInSlot(Size size, int shmem_slot)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +271,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_slot].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_slot].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_slot].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_slot].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (void *) ((char *) ShmemBase + newStart);
+	newSpace = (void *) ((char *) Segments[shmem_slot].ShmemBase + newStart);
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +298,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSlot(addr, MAIN_SHMEM_SLOT);
+}
+
+bool
+ShmemAddrIsValidInSlot(const void *addr, int shmem_slot)
+{
+	return (addr >= Segments[shmem_slot].ShmemBase) && (addr < Segments[shmem_slot].ShmemEnd);
 }
 
 /*
@@ -334,6 +365,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSlot(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SLOT);
+}
+
+HTAB *
+ShmemInitHashInSlot(const char *name,		/* table string name for shmem index */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table */
+			  HASHCTL *infoP,	/* info about key and bucket size */
+			  int hash_flags,	/* info about infoP */
+			  int shmem_slot) 	/* in which slot to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +393,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSlot(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_slot);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +428,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSlot(name, size, foundPtr, MAIN_SHMEM_SLOT);
+}
+
+void *
+ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+					  int shmem_slot)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +443,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_slot].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +466,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSlot(size, shmem_slot);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +483,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in slot %d",
+						name, shmem_slot)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +509,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSlot(size, &allocated_size, shmem_slot);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +528,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSlot(structPtr, shmem_slot));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -545,7 +594,7 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SLOT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +606,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SLOT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index e765754d80..fb0c33bf17 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SLOT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index f190e6e5e4..aef80e049b 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -23,6 +23,7 @@
 #include "storage/latch.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index b2d062781e..be4b131288 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_slot);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index dfef79ac96..081fffaf16 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_slot);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 3065ff5be7..e968deeef7 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+// Number of available slots for anonymous memory mappings
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main slot, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SLOT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 842989111c..d3e9cc721d 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -28,15 +28,25 @@
 /* shmem.c */
 extern PGDLLIMPORT slock_t *ShmemLock;
 extern void InitShmemAccess(void *seghdr);
+extern void InitShmemAccessInSlot(void *seghdr, int shmem_slot);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSlot(int shmem_slot);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSlot(Size size, int shmem_slot);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSlot(Size size, int shmem_slot);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSlot(const void *addr, int shmem_slot);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSlot(const char *name, long init_size, long max_size,
+						   HASHCTL *infoP, int hash_flags, int shmem_slot);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSlot(const char *name, Size size, bool *foundPtr,
+								   int shmem_slot);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 2488058dc356a43455b21a099ea879fff9266634
-- 
2.45.1


--ers4zrhjqnhlajas
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v2 1/6] Allow to use multiple shared memory mappings
@ 2025-02-19 16:43 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-19 16:43 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c       |   4 +-
 src/backend/port/sysv_sema.c        |   4 +-
 src/backend/port/sysv_shmem.c       | 138 ++++++++++++++++++---------
 src/backend/port/win32_sema.c       |   2 +-
 src/backend/storage/ipc/ipc.c       |   4 +-
 src/backend/storage/ipc/ipci.c      |  63 +++++++------
 src/backend/storage/ipc/shmem.c     | 141 +++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c   |   5 +-
 src/include/storage/buf_internals.h |   1 +
 src/include/storage/ipc.h           |   2 +-
 src/include/storage/pg_sema.h       |   2 +-
 src/include/storage/pg_shmem.h      |  18 ++++
 src/include/storage/shmem.h         |  12 +++
 13 files changed, 272 insertions(+), 124 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..843b1b3220f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	void *shmem; 				/* Pointer to the start of the mapped memory */
+	void *seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index e4d5b944e12..9d526eb43fd 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 174eed70367..4f6c707c204 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -85,7 +85,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -204,33 +204,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -360,7 +365,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f1e74f184f1..40aa4014b5f 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -81,6 +81,7 @@
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/spin.h"
@@ -607,9 +608,9 @@ LWLockNewTrancheId(void)
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
 	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 1a65342177d..4595f5a9676 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -22,6 +22,7 @@
 #include "storage/condition_variable.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
+#include "storage/pg_shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
 #include "utils/relcache.h"
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index e0f5f92e947..c0439f2206b 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 80d7f990496b1c7be61d9a00a2635b7d96b96197
-- 
2.45.1


--q7xzg7duksj2kgko
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v2-0002-Allow-placing-shared-memory-mapping-with-an-offse.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v4 1/8] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 ++++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 141 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 278 insertions(+), 125 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index f7c8638aec5..b6301463ac7 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -313,7 +313,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -334,7 +334,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index 895a43fb39e..389abc82519 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -75,19 +75,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 
 /*
@@ -96,9 +96,17 @@ static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -109,7 +117,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -118,9 +132,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -145,11 +159,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -179,6 +199,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -198,22 +224,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -231,6 +257,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -241,19 +273,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -268,7 +300,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -329,6 +367,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -345,9 +395,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -380,6 +430,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -388,7 +445,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -411,7 +468,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -428,8 +485,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -454,7 +511,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -473,14 +530,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -537,10 +593,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -552,15 +609,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 3df29658f18..8241c061507 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index b99ebc9e86f..138078c29c5 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -90,4 +105,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 904a336b851..5929f140236 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 

base-commit: 5e1915439085014140314979c4dd5e23bd677cac
-- 
2.45.1


--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v4-0002-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread

* [PATCH v5 04/10] Allow to use multiple shared memory mappings
@ 2025-02-28 18:54 Dmitrii Dolgov <9erthalion6@gmail.com>
  0 siblings, 0 replies; 429+ messages in thread

From: Dmitrii Dolgov @ 2025-02-28 18:54 UTC (permalink / raw)

Currently all the work with shared memory is done via a single anonymous
memory mapping, which limits ways how the shared memory could be organized.

Introduce possibility to allocate multiple shared memory mappings, where
a single mapping is associated with a specified shared memory segment.
There is only fixed amount of available segments, currently only one
main shared memory segment is allocated. A new shared memory API is
introduces, extended with a segment as a new parameter. As a path of
least resistance, the original API is kept in place, utilizing the main
shared memory segment.
---
 src/backend/port/posix_sema.c     |   4 +-
 src/backend/port/sysv_sema.c      |   4 +-
 src/backend/port/sysv_shmem.c     | 138 +++++++++++++++++++---------
 src/backend/port/win32_sema.c     |   2 +-
 src/backend/storage/ipc/ipc.c     |   4 +-
 src/backend/storage/ipc/ipci.c    |  63 +++++++------
 src/backend/storage/ipc/shmem.c   | 148 +++++++++++++++++++++---------
 src/backend/storage/lmgr/lwlock.c |  13 ++-
 src/include/storage/ipc.h         |   2 +-
 src/include/storage/pg_sema.h     |   2 +-
 src/include/storage/pg_shmem.h    |  18 ++++
 src/include/storage/shmem.h       |  12 +++
 12 files changed, 284 insertions(+), 126 deletions(-)

diff --git a/src/backend/port/posix_sema.c b/src/backend/port/posix_sema.c
index 269c7460817..401e1113fa1 100644
--- a/src/backend/port/posix_sema.c
+++ b/src/backend/port/posix_sema.c
@@ -193,7 +193,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * we don't have to expose the counters to other processes.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -220,7 +220,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 #endif
 
 	numSems = 0;
diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c
index 423b2b4f9d6..4ce2cfb662b 100644
--- a/src/backend/port/sysv_sema.c
+++ b/src/backend/port/sysv_sema.c
@@ -307,7 +307,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * have clobbered.)
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	struct stat statbuf;
 
@@ -328,7 +328,7 @@ PGReserveSemaphores(int maxSemas)
 	 * ShmemAlloc() won't be ready yet.
 	 */
 	sharedSemas = (PGSemaphore)
-		ShmemAllocUnlocked(PGSemaphoreShmemSize(maxSemas));
+		ShmemAllocUnlockedInSegment(PGSemaphoreShmemSize(maxSemas), shmem_segment);
 	numSharedSemas = 0;
 	maxSharedSemas = maxSemas;
 
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 197926d44f6..56af0231d24 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -94,8 +94,19 @@ typedef enum
 unsigned long UsedShmemSegID = 0;
 void	   *UsedShmemSegAddr = NULL;
 
-static Size AnonymousShmemSize;
-static void *AnonymousShmem = NULL;
+typedef struct AnonymousMapping
+{
+	int shmem_segment;
+	Size shmem_size; 			/* Size of the mapping */
+	Pointer shmem; 				/* Pointer to the start of the mapped memory */
+	Pointer seg_addr; 			/* SysV shared memory for the header */
+	unsigned long seg_id; 		/* IPC key */
+} AnonymousMapping;
+
+static AnonymousMapping Mappings[ANON_MAPPINGS];
+
+/* Keeps track of used mapping segments */
+static int next_free_segment = 0;
 
 static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
 static void IpcMemoryDetach(int status, Datum shmaddr);
@@ -104,6 +115,28 @@ static IpcMemoryState PGSharedMemoryAttach(IpcMemoryId shmId,
 										   void *attachAt,
 										   PGShmemHeader **addr);
 
+static const char*
+MappingName(int shmem_segment)
+{
+	switch (shmem_segment)
+	{
+		case MAIN_SHMEM_SEGMENT:
+			return "main";
+		default:
+			return "unknown";
+	}
+}
+
+static void
+DebugMappings()
+{
+	for(int i = 0; i < next_free_segment; i++)
+	{
+		AnonymousMapping m = Mappings[i];
+		elog(DEBUG1, "Mapping[%s]: addr %p, size %zu",
+			 MappingName(i), m.shmem, m.shmem_size);
+	}
+}
 
 /*
  *	InternalIpcMemoryCreate(memKey, size)
@@ -591,14 +624,13 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
- * Pass the requested size in *size.  This function will modify *size to the
- * actual size of the allocation, if it ends up allocating a segment that is
- * larger than requested.
+ * This function will modify mapping size to the actual size of the allocation,
+ * if it ends up allocating a segment that is larger than requested.
  */
-static void *
-CreateAnonymousSegment(Size *size)
+static void
+CreateAnonymousSegment(AnonymousMapping *mapping)
 {
-	Size		allocsize = *size;
+	Size		allocsize = mapping->shmem_size;
 	void	   *ptr = MAP_FAILED;
 	int			mmap_errno = 0;
 
@@ -623,8 +655,11 @@ CreateAnonymousSegment(Size *size)
 				   PG_MMAP_FLAGS | mmap_flags, -1, 0);
 		mmap_errno = errno;
 		if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
-			elog(DEBUG1, "mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
-				 allocsize);
+		{
+			DebugMappings();
+			elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
+				 MappingName(mapping->shmem_segment), allocsize);
+		}
 	}
 #endif
 
@@ -642,7 +677,7 @@ CreateAnonymousSegment(Size *size)
 		 * Use the original size, not the rounded-up value, when falling back
 		 * to non-huge pages.
 		 */
-		allocsize = *size;
+		allocsize = mapping->shmem_size;
 		ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
 				   PG_MMAP_FLAGS, -1, 0);
 		mmap_errno = errno;
@@ -651,8 +686,10 @@ CreateAnonymousSegment(Size *size)
 	if (ptr == MAP_FAILED)
 	{
 		errno = mmap_errno;
+		DebugMappings();
 		ereport(FATAL,
-				(errmsg("could not map anonymous shared memory: %m"),
+				(errmsg("segment[%s]: could not map anonymous shared memory: %m",
+						MappingName(mapping->shmem_segment)),
 				 (mmap_errno == ENOMEM) ?
 				 errhint("This error usually means that PostgreSQL's request "
 						 "for a shared memory segment exceeded available memory, "
@@ -663,8 +700,8 @@ CreateAnonymousSegment(Size *size)
 						 allocsize) : 0));
 	}
 
-	*size = allocsize;
-	return ptr;
+	mapping->shmem = ptr;
+	mapping->shmem_size = allocsize;
 }
 
 /*
@@ -674,13 +711,18 @@ CreateAnonymousSegment(Size *size)
 static void
 AnonymousShmemDetach(int status, Datum arg)
 {
-	/* Release anonymous shared memory block, if any. */
-	if (AnonymousShmem != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		AnonymousMapping m = Mappings[i];
+
+		/* Release anonymous shared memory block, if any. */
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
 
@@ -705,6 +747,7 @@ PGSharedMemoryCreate(Size size,
 	PGShmemHeader *hdr;
 	struct stat statbuf;
 	Size		sysvsize;
+	AnonymousMapping *mapping = &Mappings[next_free_segment];
 
 	/*
 	 * We use the data directory's ID info (inode and device numbers) to
@@ -733,11 +776,15 @@ PGSharedMemoryCreate(Size size,
 
 	/* Room for a header? */
 	Assert(size > MAXALIGN(sizeof(PGShmemHeader)));
+	mapping->shmem_size = size;
+	mapping->shmem_segment = next_free_segment;
 
 	if (shared_memory_type == SHMEM_TYPE_MMAP)
 	{
-		AnonymousShmem = CreateAnonymousSegment(&size);
-		AnonymousShmemSize = size;
+		/* On success, mapping data will be modified. */
+		CreateAnonymousSegment(mapping);
+
+		next_free_segment++;
 
 		/* Register on-exit routine to unmap the anonymous segment */
 		on_shmem_exit(AnonymousShmemDetach, (Datum) 0);
@@ -760,7 +807,7 @@ PGSharedMemoryCreate(Size size,
 	 * loop simultaneously.  (CreateDataDirLockFile() does not entirely ensure
 	 * that, but prefer fixing it over coping here.)
 	 */
-	NextShmemSegID = statbuf.st_ino;
+	NextShmemSegID = statbuf.st_ino + next_free_segment;
 
 	for (;;)
 	{
@@ -852,13 +899,13 @@ PGSharedMemoryCreate(Size size,
 	/*
 	 * Initialize space allocation status for segment.
 	 */
-	hdr->totalsize = size;
+	hdr->totalsize = mapping->shmem_size;
 	hdr->freeoffset = MAXALIGN(sizeof(PGShmemHeader));
 	*shim = hdr;
 
 	/* Save info for possible future use */
-	UsedShmemSegAddr = memAddress;
-	UsedShmemSegID = (unsigned long) NextShmemSegID;
+	mapping->seg_addr = memAddress;
+	mapping->seg_id = (unsigned long) NextShmemSegID;
 
 	/*
 	 * If AnonymousShmem is NULL here, then we're not using anonymous shared
@@ -866,10 +913,10 @@ PGSharedMemoryCreate(Size size,
 	 * block. Otherwise, the System V shared memory block is only a shim, and
 	 * we must return a pointer to the real block.
 	 */
-	if (AnonymousShmem == NULL)
+	if (mapping->shmem == NULL)
 		return hdr;
-	memcpy(AnonymousShmem, hdr, sizeof(PGShmemHeader));
-	return (PGShmemHeader *) AnonymousShmem;
+	memcpy(mapping->shmem, hdr, sizeof(PGShmemHeader));
+	return (PGShmemHeader *) mapping->shmem;
 }
 
 #ifdef EXEC_BACKEND
@@ -969,23 +1016,28 @@ PGSharedMemoryNoReAttach(void)
 void
 PGSharedMemoryDetach(void)
 {
-	if (UsedShmemSegAddr != NULL)
+	for(int i = 0; i < next_free_segment; i++)
 	{
-		if ((shmdt(UsedShmemSegAddr) < 0)
+		AnonymousMapping m = Mappings[i];
+
+		if (m.seg_addr != NULL)
+		{
+			if ((shmdt(m.seg_addr) < 0)
 #if defined(EXEC_BACKEND) && defined(__CYGWIN__)
-		/* Work-around for cygipc exec bug */
-			&& shmdt(NULL) < 0
+			/* Work-around for cygipc exec bug */
+				&& shmdt(NULL) < 0
 #endif
-			)
-			elog(LOG, "shmdt(%p) failed: %m", UsedShmemSegAddr);
-		UsedShmemSegAddr = NULL;
-	}
+				)
+				elog(LOG, "shmdt(%p) failed: %m", m.seg_addr);
+			m.seg_addr = NULL;
+		}
 
-	if (AnonymousShmem != NULL)
-	{
-		if (munmap(AnonymousShmem, AnonymousShmemSize) < 0)
-			elog(LOG, "munmap(%p, %zu) failed: %m",
-				 AnonymousShmem, AnonymousShmemSize);
-		AnonymousShmem = NULL;
+		if (m.shmem != NULL)
+		{
+			if (munmap(m.shmem, m.shmem_size) < 0)
+				elog(LOG, "munmap(%p, %zu) failed: %m",
+					 m.shmem, m.shmem_size);
+			m.shmem = NULL;
+		}
 	}
 }
diff --git a/src/backend/port/win32_sema.c b/src/backend/port/win32_sema.c
index 5854ad1f54d..e7365ff8060 100644
--- a/src/backend/port/win32_sema.c
+++ b/src/backend/port/win32_sema.c
@@ -44,7 +44,7 @@ PGSemaphoreShmemSize(int maxSemas)
  * process exits.
  */
 void
-PGReserveSemaphores(int maxSemas)
+PGReserveSemaphores(int maxSemas, int shmem_segment)
 {
 	mySemSet = (HANDLE *) malloc(maxSemas * sizeof(HANDLE));
 	if (mySemSet == NULL)
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
index 567739b5be9..5b55bec8d9d 100644
--- a/src/backend/storage/ipc/ipc.c
+++ b/src/backend/storage/ipc/ipc.c
@@ -61,6 +61,8 @@ static void proc_exit_prepare(int code);
  * but provide some additional features we need --- in particular,
  * we want to register callbacks to invoke when we are disconnecting
  * from a broken shared-memory context but not exiting the postmaster.
+ * Maximum number of such exit callbacks depends on the number of shared
+ * segments.
  *
  * Callback functions can take zero, one, or two args: the first passed
  * arg is the integer exitcode, the second is the Datum supplied when
@@ -68,7 +70,7 @@ static void proc_exit_prepare(int code);
  * ----------------------------------------------------------------
  */
 
-#define MAX_ON_EXITS 20
+#define MAX_ON_EXITS 40
 
 struct ONEXIT
 {
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 2fa045e6b0f..8b38e985327 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -86,7 +86,7 @@ RequestAddinShmemSpace(Size size)
  * required.
  */
 Size
-CalculateShmemSize(int *num_semaphores)
+CalculateShmemSize(int *num_semaphores, int shmem_segment)
 {
 	Size		size;
 	int			numSemas;
@@ -206,33 +206,38 @@ CreateSharedMemoryAndSemaphores(void)
 
 	Assert(!IsUnderPostmaster);
 
-	/* Compute the size of the shared-memory block */
-	size = CalculateShmemSize(&numSemas);
-	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
-
-	/*
-	 * Create the shmem segment
-	 */
-	seghdr = PGSharedMemoryCreate(size, &shim);
-
-	/*
-	 * Make sure that huge pages are never reported as "unknown" while the
-	 * server is running.
-	 */
-	Assert(strcmp("unknown",
-				  GetConfigOption("huge_pages_status", false, false)) != 0);
-
-	InitShmemAccess(seghdr);
-
-	/*
-	 * Create semaphores
-	 */
-	PGReserveSemaphores(numSemas);
-
-	/*
-	 * Set up shared memory allocation mechanism
-	 */
-	InitShmemAllocation();
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		/* Compute the size of the shared-memory block */
+		size = CalculateShmemSize(&numSemas, segment);
+		elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);
+
+		/*
+		 * Create the shmem segment.
+		 *
+		 * XXX: Do multiple shims are needed, one per segment?
+		 */
+		seghdr = PGSharedMemoryCreate(size, &shim);
+
+		/*
+		 * Make sure that huge pages are never reported as "unknown" while the
+		 * server is running.
+		 */
+		Assert(strcmp("unknown",
+					  GetConfigOption("huge_pages_status", false, false)) != 0);
+
+		InitShmemAccessInSegment(seghdr, segment);
+
+		/*
+		 * Create semaphores
+		 */
+		PGReserveSemaphores(numSemas, segment);
+
+		/*
+		 * Set up shared memory allocation mechanism
+		 */
+		InitShmemAllocationInSegment(segment);
+	}
 
 	/* Initialize subsystems */
 	CreateOrAttachShmemStructs();
@@ -363,7 +368,7 @@ InitializeShmemGUCs(void)
 	/*
 	 * Calculate the shared memory size and round up to the nearest megabyte.
 	 */
-	size_b = CalculateShmemSize(&num_semas);
+	size_b = CalculateShmemSize(&num_semas, MAIN_SHMEM_SEGMENT);
 	size_mb = add_size(size_b, (1024 * 1024) - 1) / (1024 * 1024);
 	sprintf(buf, "%zu", size_mb);
 	SetConfigOption("shared_memory_size", buf,
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index c9ae3b45b76..72255a1c5ca 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -76,19 +76,19 @@
 #include "utils/builtins.h"
 
 static void *ShmemAllocRaw(Size size, Size *allocated_size);
+static void *ShmemAllocRawInSegment(Size size, Size *allocated_size,
+								 int shmem_segment);
 
 /* shared memory global variables */
 
-static PGShmemHeader *ShmemSegHdr;	/* shared mem segment header */
+ShmemSegment Segments[ANON_MAPPINGS];
 
-static void *ShmemBase;			/* start address of shared memory */
-
-static void *ShmemEnd;			/* end+1 address of shared memory */
-
-slock_t    *ShmemLock;			/* spinlock for shared memory and LWLock
-								 * allocation */
-
-static HTAB *ShmemIndex = NULL; /* primary index hashtable for shmem */
+/*
+ * Primary index hashtable for shmem, for simplicity we use a single for all
+ * shared memory segments. There can be performance consequences of that, and
+ * an alternative option would be to have one index per shared memory segments.
+ */
+static HTAB *ShmemIndex = NULL;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
 static bool firstNumaTouch = true;
@@ -101,9 +101,17 @@ Datum		pg_numa_available(PG_FUNCTION_ARGS);
 void
 InitShmemAccess(PGShmemHeader *seghdr)
 {
-	ShmemSegHdr = seghdr;
-	ShmemBase = seghdr;
-	ShmemEnd = (char *) ShmemBase + seghdr->totalsize;
+	InitShmemAccessInSegment(seghdr, MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAccessInSegment(PGShmemHeader *seghdr, int shmem_segment)
+{
+	PGShmemHeader *shmhdr = (PGShmemHeader *) seghdr;
+	ShmemSegment *seg = &Segments[shmem_segment];
+	seg->ShmemSegHdr = shmhdr;
+	seg->ShmemBase = (void *) shmhdr;
+	seg->ShmemEnd = (char *) seg->ShmemBase + shmhdr->totalsize;
 }
 
 /*
@@ -114,7 +122,13 @@ InitShmemAccess(PGShmemHeader *seghdr)
 void
 InitShmemAllocation(void)
 {
-	PGShmemHeader *shmhdr = ShmemSegHdr;
+	InitShmemAllocationInSegment(MAIN_SHMEM_SEGMENT);
+}
+
+void
+InitShmemAllocationInSegment(int shmem_segment)
+{
+	PGShmemHeader *shmhdr = Segments[shmem_segment].ShmemSegHdr;
 	char	   *aligned;
 
 	Assert(shmhdr != NULL);
@@ -123,9 +137,9 @@ InitShmemAllocation(void)
 	 * Initialize the spinlock used by ShmemAlloc.  We must use
 	 * ShmemAllocUnlocked, since obviously ShmemAlloc can't be called yet.
 	 */
-	ShmemLock = (slock_t *) ShmemAllocUnlocked(sizeof(slock_t));
+	Segments[shmem_segment].ShmemLock = (slock_t *) ShmemAllocUnlockedInSegment(sizeof(slock_t), shmem_segment);
 
-	SpinLockInit(ShmemLock);
+	SpinLockInit(Segments[shmem_segment].ShmemLock);
 
 	/*
 	 * Allocations after this point should go through ShmemAlloc, which
@@ -150,11 +164,17 @@ InitShmemAllocation(void)
  */
 void *
 ShmemAlloc(Size size)
+{
+	return ShmemAllocInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocInSegment(Size size, int shmem_segment)
 {
 	void	   *newSpace;
 	Size		allocated_size;
 
-	newSpace = ShmemAllocRaw(size, &allocated_size);
+	newSpace = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 	if (!newSpace)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -184,6 +204,12 @@ ShmemAllocNoError(Size size)
  */
 static void *
 ShmemAllocRaw(Size size, Size *allocated_size)
+{
+	return ShmemAllocRawInSegment(size, allocated_size, MAIN_SHMEM_SEGMENT);
+}
+
+static void *
+ShmemAllocRawInSegment(Size size, Size *allocated_size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -203,22 +229,22 @@ ShmemAllocRaw(Size size, Size *allocated_size)
 	size = CACHELINEALIGN(size);
 	*allocated_size = size;
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	SpinLockAcquire(ShmemLock);
+	SpinLockAcquire(Segments[shmem_segment].ShmemLock);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree <= ShmemSegHdr->totalsize)
+	if (newFree <= Segments[shmem_segment].ShmemSegHdr->totalsize)
 	{
-		newSpace = (char *) ShmemBase + newStart;
-		ShmemSegHdr->freeoffset = newFree;
+		newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
+		Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 	}
 	else
 		newSpace = NULL;
 
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[shmem_segment].ShmemLock);
 
 	/* note this assert is okay with newSpace == NULL */
 	Assert(newSpace == (void *) CACHELINEALIGN(newSpace));
@@ -236,6 +262,12 @@ ShmemAllocRaw(Size size, Size *allocated_size)
  */
 void *
 ShmemAllocUnlocked(Size size)
+{
+	return ShmemAllocUnlockedInSegment(size, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemAllocUnlockedInSegment(Size size, int shmem_segment)
 {
 	Size		newStart;
 	Size		newFree;
@@ -246,19 +278,19 @@ ShmemAllocUnlocked(Size size)
 	 */
 	size = MAXALIGN(size);
 
-	Assert(ShmemSegHdr != NULL);
+	Assert(Segments[shmem_segment].ShmemSegHdr != NULL);
 
-	newStart = ShmemSegHdr->freeoffset;
+	newStart = Segments[shmem_segment].ShmemSegHdr->freeoffset;
 
 	newFree = newStart + size;
-	if (newFree > ShmemSegHdr->totalsize)
+	if (newFree > Segments[shmem_segment].ShmemSegHdr->totalsize)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory (%zu bytes requested)",
 						size)));
-	ShmemSegHdr->freeoffset = newFree;
+	Segments[shmem_segment].ShmemSegHdr->freeoffset = newFree;
 
-	newSpace = (char *) ShmemBase + newStart;
+	newSpace = (char *) Segments[shmem_segment].ShmemBase + newStart;
 
 	Assert(newSpace == (void *) MAXALIGN(newSpace));
 
@@ -273,7 +305,13 @@ ShmemAllocUnlocked(Size size)
 bool
 ShmemAddrIsValid(const void *addr)
 {
-	return (addr >= ShmemBase) && (addr < ShmemEnd);
+	return ShmemAddrIsValidInSegment(addr, MAIN_SHMEM_SEGMENT);
+}
+
+bool
+ShmemAddrIsValidInSegment(const void *addr, int shmem_segment)
+{
+	return (addr >= Segments[shmem_segment].ShmemBase) && (addr < Segments[shmem_segment].ShmemEnd);
 }
 
 /*
@@ -334,6 +372,18 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 			  long max_size,	/* max size of the table */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
+{
+	return ShmemInitHashInSegment(name, init_size, max_size, infoP, hash_flags,
+							   MAIN_SHMEM_SEGMENT);
+}
+
+HTAB *
+ShmemInitHashInSegment(const char *name,		/* table string name for shmem index */
+			  long init_size,		/* initial table size */
+			  long max_size,		/* max size of the table */
+			  HASHCTL *infoP,		/* info about key and bucket size */
+			  int hash_flags,		/* info about infoP */
+			  int shmem_segment) 	/* in which segment to keep the table */
 {
 	bool		found;
 	void	   *location;
@@ -350,9 +400,9 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
 	hash_flags |= HASH_SHARED_MEM | HASH_ALLOC | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name,
+	location = ShmemInitStructInSegment(name,
 							   hash_get_shared_size(infoP, hash_flags),
-							   &found);
+							   &found, shmem_segment);
 
 	/*
 	 * if it already exists, attach to it rather than allocate and initialize
@@ -385,6 +435,13 @@ ShmemInitHash(const char *name,		/* table string name for shmem index */
  */
 void *
 ShmemInitStruct(const char *name, Size size, bool *foundPtr)
+{
+	return ShmemInitStructInSegment(name, size, foundPtr, MAIN_SHMEM_SEGMENT);
+}
+
+void *
+ShmemInitStructInSegment(const char *name, Size size, bool *foundPtr,
+					  int shmem_segment)
 {
 	ShmemIndexEnt *result;
 	void	   *structPtr;
@@ -393,7 +450,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	if (!ShmemIndex)
 	{
-		PGShmemHeader *shmemseghdr = ShmemSegHdr;
+		PGShmemHeader *shmemseghdr = Segments[shmem_segment].ShmemSegHdr;
 
 		/* Must be trying to create/attach to ShmemIndex itself */
 		Assert(strcmp(name, "ShmemIndex") == 0);
@@ -416,7 +473,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 			 * process can be accessing shared memory yet.
 			 */
 			Assert(shmemseghdr->index == NULL);
-			structPtr = ShmemAlloc(size);
+			structPtr = ShmemAllocInSegment(size, shmem_segment);
 			shmemseghdr->index = structPtr;
 			*foundPtr = false;
 		}
@@ -433,8 +490,8 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		LWLockRelease(ShmemIndexLock);
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create ShmemIndex entry for data structure \"%s\"",
-						name)));
+				 errmsg("could not create ShmemIndex entry for data structure \"%s\" in segment %d",
+						name, shmem_segment)));
 	}
 
 	if (*foundPtr)
@@ -459,7 +516,7 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 		Size		allocated_size;
 
 		/* It isn't in the table yet. allocate and initialize it */
-		structPtr = ShmemAllocRaw(size, &allocated_size);
+		structPtr = ShmemAllocRawInSegment(size, &allocated_size, shmem_segment);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -478,14 +535,13 @@ ShmemInitStruct(const char *name, Size size, bool *foundPtr)
 
 	LWLockRelease(ShmemIndexLock);
 
-	Assert(ShmemAddrIsValid(structPtr));
+	Assert(ShmemAddrIsValidInSegment(structPtr, shmem_segment));
 
 	Assert(structPtr == (void *) CACHELINEALIGN(structPtr));
 
 	return structPtr;
 }
 
-
 /*
  * Add two Size values, checking for overflow
  */
@@ -542,10 +598,11 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 
 	/* output all allocated entries */
 	memset(nulls, 0, sizeof(nulls));
+	/* XXX: take all shared memory segments into account. */
 	while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL)
 	{
 		values[0] = CStringGetTextDatum(ent->key);
-		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
+		values[1] = Int64GetDatum((char *) ent->location - (char *) Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
 		named_allocated += ent->allocated_size;
@@ -557,15 +614,15 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	/* output shared memory allocated but not counted via the shmem index */
 	values[0] = CStringGetTextDatum("<anonymous>");
 	nulls[1] = true;
-	values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset - named_allocated);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
 	nulls[0] = true;
-	values[1] = Int64GetDatum(ShmemSegHdr->freeoffset);
+	values[1] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	nulls[1] = false;
-	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset);
+	values[2] = Int64GetDatum(Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->totalsize - Segments[MAIN_SHMEM_SEGMENT].ShmemSegHdr->freeoffset);
 	values[3] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
@@ -630,7 +687,12 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 	 * this is not very likely, and moreover we have more entries, each of
 	 * them using only fraction of the total pages.
 	 */
-	shm_total_page_count = (ShmemSegHdr->totalsize / os_page_size) + 1;
+	for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+	{
+		PGShmemHeader *shmhdr = Segments[segment].ShmemSegHdr;
+		shm_total_page_count += (shmhdr->totalsize / os_page_size) + 1;
+	}
+
 	page_ptrs = palloc0(sizeof(void *) * shm_total_page_count);
 	pages_status = palloc(sizeof(int) * shm_total_page_count);
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 46f44bc4511..a36b08895c8 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -80,6 +80,8 @@
 #include "pg_trace.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
+#include "postmaster/postmaster.h"
+#include "storage/pg_shmem.h"
 #include "storage/proc.h"
 #include "storage/proclist.h"
 #include "storage/procnumber.h"
@@ -618,10 +620,15 @@ LWLockNewTrancheId(void)
 	int		   *LWLockCounter;
 
 	LWLockCounter = (int *) ((char *) MainLWLockArray - sizeof(int));
-	/* We use the ShmemLock spinlock to protect LWLockCounter */
-	SpinLockAcquire(ShmemLock);
+	/*
+	 * We use the ShmemLock spinlock to protect LWLockCounter.
+	 *
+	 * XXX: Looks like this is the only use of Segments outside of shmem.c,
+	 * it's maybe worth it to reshape this part to hide Segments structure.
+	 */
+	SpinLockAcquire(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 	result = (*LWLockCounter)++;
-	SpinLockRelease(ShmemLock);
+	SpinLockRelease(Segments[MAIN_SHMEM_SEGMENT].ShmemLock);
 
 	return result;
 }
diff --git a/src/include/storage/ipc.h b/src/include/storage/ipc.h
index 3baf418b3d1..6ebda479ced 100644
--- a/src/include/storage/ipc.h
+++ b/src/include/storage/ipc.h
@@ -77,7 +77,7 @@ extern void check_on_shmem_exit_lists_are_empty(void);
 /* ipci.c */
 extern PGDLLIMPORT shmem_startup_hook_type shmem_startup_hook;
 
-extern Size CalculateShmemSize(int *num_semaphores);
+extern Size CalculateShmemSize(int *num_semaphores, int shmem_segment);
 extern void CreateSharedMemoryAndSemaphores(void);
 #ifdef EXEC_BACKEND
 extern void AttachSharedMemoryStructs(void);
diff --git a/src/include/storage/pg_sema.h b/src/include/storage/pg_sema.h
index fa6ca35a51f..8ae9637fcd0 100644
--- a/src/include/storage/pg_sema.h
+++ b/src/include/storage/pg_sema.h
@@ -41,7 +41,7 @@ typedef HANDLE PGSemaphore;
 extern Size PGSemaphoreShmemSize(int maxSemas);
 
 /* Module initialization (called during postmaster start or shmem reinit) */
-extern void PGReserveSemaphores(int maxSemas);
+extern void PGReserveSemaphores(int maxSemas, int shmem_segment);
 
 /* Allocate a PGSemaphore structure with initial count 1 */
 extern PGSemaphore PGSemaphoreCreate(void);
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 5f7d4b83a60..2348c59b5a0 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -25,6 +25,7 @@
 #define PG_SHMEM_H
 
 #include "storage/dsm_impl.h"
+#include "storage/spin.h"
 
 typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 {
@@ -41,6 +42,20 @@ typedef struct PGShmemHeader	/* standard header for all Postgres shmem */
 #endif
 } PGShmemHeader;
 
+typedef struct ShmemSegment
+{
+	PGShmemHeader *ShmemSegHdr; 	/* shared mem segment header */
+	void *ShmemBase; 				/* start address of shared memory */
+	void *ShmemEnd; 				/* end+1 address of shared memory */
+	slock_t    *ShmemLock; 			/* spinlock for shared memory and LWLock
+									 * allocation */
+} ShmemSegment;
+
+/* Number of available segments for anonymous memory mappings */
+#define ANON_MAPPINGS 1
+
+extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
+
 /* GUC variables */
 extern PGDLLIMPORT int shared_memory_type;
 extern PGDLLIMPORT int huge_pages;
@@ -91,4 +106,7 @@ extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
 
+/* The main segment, contains everything except buffer blocks and related data. */
+#define MAIN_SHMEM_SEGMENT 0
+
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index c1f668ded95..69663d412c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -29,15 +29,27 @@
 extern PGDLLIMPORT slock_t *ShmemLock;
 struct PGShmemHeader;			/* avoid including storage/pg_shmem.h here */
 extern void InitShmemAccess(struct PGShmemHeader *seghdr);
+extern void InitShmemAccessInSegment(struct PGShmemHeader *seghdr,
+									 int shmem_segment);
 extern void InitShmemAllocation(void);
+extern void InitShmemAllocationInSegment(int shmem_segment);
 extern void *ShmemAlloc(Size size);
+extern void *ShmemAllocInSegment(Size size, int shmem_segment);
 extern void *ShmemAllocNoError(Size size);
 extern void *ShmemAllocUnlocked(Size size);
+extern void *ShmemAllocUnlockedInSegment(Size size, int shmem_segment);
 extern bool ShmemAddrIsValid(const void *addr);
+extern bool ShmemAddrIsValidInSegment(const void *addr, int shmem_segment);
 extern void InitShmemIndex(void);
+extern void InitVariableShmemIndex(void);
 extern HTAB *ShmemInitHash(const char *name, long init_size, long max_size,
 						   HASHCTL *infoP, int hash_flags);
+extern HTAB *ShmemInitHashInSegment(const char *name, long init_size,
+									long max_size, HASHCTL *infoP,
+									int hash_flags, int shmem_segment);
 extern void *ShmemInitStruct(const char *name, Size size, bool *foundPtr);
+extern void *ShmemInitStructInSegment(const char *name, Size size,
+									  bool *foundPtr, int shmem_segment);
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
 
-- 
2.49.0


--bwrlgp6w2ubxykjq
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v5-0005-Address-space-reservation-for-shared-memory.patch"



^ permalink  raw  reply  [nested|flat] 429+ messages in thread


end of thread, other threads:[~2025-02-28 18:54 UTC | newest]

Thread overview: 429+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-09-01 23:36 Re: logtape.c stats don't account for unused "prefetched" block numbers Alvaro Herrera <alvherre@2ndquadrant.com>
2020-09-02 00:24 ` Peter Geoghegan <pg@bowt.ie>
2020-09-05 19:03   ` Peter Geoghegan <pg@bowt.ie>
2020-09-08 17:27     ` Jeff Davis <pgsql@j-davis.com>
2020-09-09 06:28     ` Jeff Davis <pgsql@j-davis.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2024-10-09 13:41 [PATCH v1 1/5] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-19 16:43 [PATCH v2 1/6] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v4 1/8] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>
2025-02-28 18:54 [PATCH v5 04/10] Allow to use multiple shared memory mappings Dmitrii Dolgov <9erthalion6@gmail.com>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox